home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / motif / textfun.c < prev    next >
C/C++ Source or Header  |  1996-11-11  |  25KB  |  846 lines

  1. /* $Revision: 1.1 $ */
  2. /* compile: cc -o textfun textfun.c -lXm -lXt -lGL -lXext -lX11 -lm */
  3.  
  4. /*
  5.  * textfun demonstrates pulling X bitmap fonts from the X server into an
  6.  * OpenGL client and converting the bitmaps into OpenGL display lists with
  7.  * transformable geometry.  Text can then be displayed from any perspective
  8.  * in 3D.
  9.  * 
  10.  * Motif is used for the user interface.  The program renders OpenGL into a
  11.  * standard Motif drawing area and does not use any special OpenGL widget.
  12.  * Pull down menus with toggles and radio buttons are used.  The animation is
  13.  * controled by X Toolkit work procs; iconfiying textfun will stop the work
  14.  * proc and resume it when the program is uniconified.
  15.  * 
  16.  * Various fonts can be switched between.  A number of the fonts are X scalable
  17.  * fonts demonstrating how the blocky nature of the text can be minimized
  18.  * with higher resolution fonts.
  19.  * 
  20.  * Mark J. Kilgard
  21.  * mjk@sgi.com
  22.  * Silicon Graphics, Inc.
  23.  * March 7, 1994
  24.  */
  25.  
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <unistd.h>
  29. #include <math.h>
  30. #ifdef IRIX_5_1_MOTIF_BUG_WORKAROUND
  31. #include <sys/utsname.h>
  32. #endif
  33. #include <Xm/MainW.h>
  34. #include <Xm/RowColumn.h>
  35. #include <Xm/PushB.h>
  36. #include <Xm/ToggleB.h>
  37. #include <Xm/CascadeB.h>
  38. #include <Xm/Frame.h>
  39. #include <Xm/DrawingA.h>
  40. #include <X11/keysym.h>
  41. #include <GL/gl.h>
  42. #include <GL/glu.h>
  43. #include <GL/glx.h>
  44.  
  45. #ifdef DEBUG
  46. #define GL_ERROR_CHECK() \
  47.     { /* for help debugging, report any OpenGL errors that occur per frame */ \
  48.         GLenum error; \
  49.         while((error = glGetError()) != GL_NO_ERROR) \
  50.             fprintf(stderr, "GL error: %s, line %d\n", gluErrorString(error), __LINE__); \
  51.     }
  52. #else
  53. #define GL_ERROR_CHECK() { /* nothing */ }
  54. #endif
  55.  
  56. typedef struct {
  57.     short           width;
  58.     short           height;
  59.     short           xoffset;
  60.     short           yoffset;
  61.     short           advance;
  62.     char           *bitmap;
  63. }               PerCharInfo, *PerCharInfoPtr;
  64.  
  65. typedef struct {
  66.     int             min_char;
  67.     int             max_char;
  68.     int             max_ascent;
  69.     int             max_descent;
  70.     GLuint          dlist_base;
  71.     PerCharInfo     glyph[1];
  72. }               FontInfo, *FontInfoPtr;
  73.  
  74. typedef struct {
  75.     char           *name;
  76.     char           *xlfd;
  77.     XFontStruct    *xfont;
  78.     FontInfoPtr     fontinfo;
  79. }               FontEntry, *FontEntryPtr;
  80.  
  81. static int      dblBuf[] =
  82. {
  83.     GLX_DOUBLEBUFFER, GLX_RGBA, GLX_DEPTH_SIZE, 16,
  84.     GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
  85.     None
  86. };
  87. static int     *snglBuf = &dblBuf[1];
  88.  
  89. static String   fallbackResources[] =
  90. {
  91.     "*sgiMode: true",        /* try to enable IRIX 5.2+ look & feel */
  92.     "*useSchemes: all",        /* and SGI schemes */
  93.     "*title: OpenGL text transformation",
  94.     "*glxarea*width: 300", "*glxarea*height: 300", NULL
  95. };
  96.  
  97. static FontEntry fontEntry[] =
  98. {
  99.     {"Fixed", "fixed", NULL, NULL},
  100.     {"Utopia", "-adobe-utopia-medium-r-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
  101.     {"Schoolbook", "-adobe-new century schoolbook-bold-i-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
  102.     {"Rock", "-sgi-rock-medium-r-normal--20-*-*-*-p-*-iso8859-1", NULL, NULL},
  103.     {"Rock (hi-res)", "-sgi-rock-medium-r-normal--50-*-*-*-p-*-iso8859-1", NULL, NULL},
  104.     {"Curl", "-sgi-curl-medium-r-normal--20-*-*-*-p-*-*-*", NULL, NULL},
  105.     {"Curl (hi-res)", "-sgi-curl-medium-r-normal--50-*-*-*-p-*-*-*", NULL, NULL},
  106.     {"Dingbats", "-adobe-itc zapf dingbats-medium-r-normal--35-*-*-*-p-*-adobe-fontspecific", NULL, NULL}
  107. };
  108. #define NUM_FONT_ENTRIES sizeof(fontEntry)/sizeof(FontEntry)
  109.  
  110. static char    *defaultMessage[] =
  111. {"OpenGL ", "rocks into", "the Future!"};
  112. #define NUM_DEFAULT_MESSAGES sizeof(defaultMessage)/sizeof(char*)
  113.  
  114. Display        *dpy;
  115. GLboolean       doubleBuffer = GL_TRUE, motion = GL_FALSE, rotation = GL_FALSE,
  116.                 wobbling = GL_FALSE, made_current = GL_FALSE, dollying = GL_TRUE;
  117. XtAppContext    app;
  118. XtWorkProcId    workId = 0;
  119. Widget          toplevel = NULL;
  120. Widget        mainw, menubar, menupane, btn, cascade, frame, glxarea;
  121. GLXContext      cx;
  122. XVisualInfo    *vi;
  123. Colormap        cmap;
  124. Arg             menuPaneArgs[4], args[1];
  125. GLfloat         theta = 0, delta = 0;
  126. GLfloat         distance = 19, angle = 0, wobble_angle = 0;
  127. GLuint          base;
  128. int             numMessages;
  129. char          **messages;
  130.  
  131. void 
  132. draw(Widget w)
  133. {
  134.     GLfloat         red, green, blue;
  135.     int             i;
  136.  
  137.     glClear(GL_DEPTH_BUFFER_BIT);
  138.  
  139.     /* paint black to blue smooth shaded polygon for background */
  140.     glDisable(GL_DEPTH_TEST);
  141.     glShadeModel(GL_SMOOTH);
  142.     glBegin(GL_POLYGON);
  143.     glColor3f(1.0, 1.0, 1.0);
  144.     glVertex3f(-20, 20, -19);
  145.     glVertex3f(20, 20, -19);
  146.     glColor3f(0.0, 0.0, 1.0);
  147.     glVertex3f(20, -20, -19);
  148.     glVertex3f(-20, -20, -19);
  149.     glEnd();
  150.  
  151.     glEnable(GL_DEPTH_TEST);
  152.     glShadeModel(GL_FLAT);
  153.  
  154.     glPushMatrix();
  155.     glTranslatef(0, 0, -distance);
  156.     glRotatef(angle, 0, 0, 1);
  157.     glRotatef(wobble_angle, 0, 1, 0);
  158.     glCallList(base);
  159.     glPopMatrix();
  160.  
  161.     if (doubleBuffer)
  162.     glXSwapBuffers(dpy, XtWindow(w));
  163.     if (!glXIsDirect(dpy, cx))
  164.     glFinish();        /* avoid indirect rendering latency from
  165.                  * queuing */
  166.     GL_ERROR_CHECK();
  167. }
  168.  
  169. void 
  170. resize(Widget w, XtPointer data, XtPointer callData)
  171. {
  172.     Dimension       width, height;
  173.  
  174.     /*
  175.      * It is possible for a drawing area widget's resize callback to be
  176.      * called before the window is realized, and therefore before we have
  177.      * made our OpenGL context to the window ID.  So only let the glViewPort
  178.      * call happen if we really have "made_current".
  179.      */
  180.     if (made_current) {
  181.     XtVaGetValues(w, XmNwidth, &width, XmNheight, &height, NULL);
  182.     glViewport(0, 0, (GLint) width, (GLint) height);
  183.     }
  184. }
  185.  
  186. void 
  187. tick(void)
  188. {
  189.     if (dollying) {
  190.     theta += 0.1;
  191.     distance = cos(theta) * 7 + 12;
  192.     }
  193.     if (rotation)
  194.     angle -= 6;
  195.     if (wobbling) {
  196.     delta += 0.1;
  197.     wobble_angle = sin(delta) * 40;
  198.     }
  199. }
  200.  
  201. Boolean 
  202. animate(XtPointer data)
  203. {
  204.     tick();
  205.     draw(glxarea);
  206.     return False;        /* leave work proc active */
  207. }
  208.  
  209. void 
  210. syncstate(void)
  211. {
  212.     if (motion && (dollying || rotation || wobbling)) {
  213.     if (workId == 0)
  214.         workId = XtAppAddWorkProc(app, animate, NULL);
  215.     } else if (workId != 0) {
  216.     XtRemoveWorkProc(workId);
  217.     workId = 0;
  218.     }
  219. }
  220.  
  221. void 
  222. toggle(void)
  223. {
  224.     motion = !motion;        /* toggle */
  225.     syncstate();
  226. }
  227.  
  228. void 
  229. dolly(void)
  230. {
  231.     dollying = !dollying;    /* toggle */
  232.     syncstate();
  233. }
  234.  
  235. void 
  236. rotate(void)
  237. {
  238.     rotation = !rotation;    /* toggle */
  239.     syncstate();
  240. }
  241.  
  242. void 
  243. wobble(void)
  244. {
  245.     wobbling = !wobbling;    /* toggle */
  246.     syncstate();
  247. }
  248.  
  249. void 
  250. quit(Widget w, XtPointer data, XtPointer callData)
  251. {
  252.     exit(0);
  253. }
  254.  
  255. void 
  256. input(Widget w, XtPointer data, XtPointer callData)
  257. {
  258.     XmDrawingAreaCallbackStruct *cd = (XmDrawingAreaCallbackStruct *) callData;
  259.     char            buf[1];
  260.     KeySym          keysym;
  261.     int             rc;
  262.  
  263.     if (cd->event->type == KeyPress)
  264.     if (XLookupString((XKeyEvent *) cd->event, buf, 1, &keysym, NULL) == 1)
  265.         switch (keysym) {
  266.         case XK_space:
  267.         if (!motion) {    /* advance one frame if not in motion */
  268.             tick();
  269.             draw(w);
  270.         }
  271.         break;
  272.         case XK_Escape:
  273.         exit(0);
  274.         }
  275. }
  276.  
  277. void 
  278. map_state_changed(Widget w, XtPointer data, XEvent * event, Boolean * cont)
  279. {
  280.     switch (event->type) {
  281.     case MapNotify:
  282.     syncstate();
  283.     break;
  284.     case UnmapNotify:
  285.     if (motion) {
  286.         XtRemoveWorkProc(workId);
  287.         workId = 0;
  288.         }
  289.     break;
  290.     }
  291. }
  292.  
  293. /* #define REPORT_GLYPHS */
  294. #ifdef REPORT_GLYPHS
  295. #define DEBUG_GLYPH4(msg,a,b,c,d) printf(msg,a,b,c,d)
  296. #define DEBUG_GLYPH(msg) printf(msg)
  297. #else
  298. #define DEBUG_GLYPH4(msg,a,b,c,d) { /* nothing */ }
  299. #define DEBUG_GLYPH(msg) { /* nothing */ }
  300. #endif
  301.  
  302. #define MAX_GLYPHS_PER_GRAB 512 /* this is big enough for 2^9 glyph character sets */
  303.  
  304. FontInfoPtr
  305. SuckGlyphsFromServer(Display * dpy, Font font)
  306. {
  307.     Pixmap          offscreen;
  308.     XFontStruct    *fontinfo;
  309.     XImage         *image;
  310.     GC              xgc;
  311.     XGCValues       values;
  312.     int             numchars;
  313.     int             width, height, pixwidth;
  314.     int             i, j;
  315.     XCharStruct    *charinfo;
  316.     XChar2b         character;
  317.     char           *bitmapData;
  318.     int             x, y;
  319.     int             spanLength;
  320.     int             charWidth, charHeight, maxSpanLength;
  321.     int             grabList[MAX_GLYPHS_PER_GRAB];
  322.     int             glyphsPerGrab = MAX_GLYPHS_PER_GRAB;
  323.     int             numToGrab, thisglyph;
  324.     FontInfoPtr     myfontinfo;
  325.  
  326.     fontinfo = XQueryFont(dpy, font);
  327.     if (!fontinfo)
  328.     return NULL;
  329.  
  330.     numchars = fontinfo->max_char_or_byte2 - fontinfo->min_char_or_byte2 + 1;
  331.     if (numchars < 1)
  332.     return NULL;
  333.  
  334.     myfontinfo = (FontInfoPtr) malloc(sizeof(FontInfo) + (numchars - 1) * sizeof(PerCharInfo));
  335.     if (!myfontinfo)
  336.     return NULL;
  337.  
  338.     myfontinfo->min_char = fontinfo->min_char_or_byte2;
  339.     myfontinfo->max_char = fontinfo->max_char_or_byte2;
  340.     myfontinfo->max_ascent = fontinfo->max_bounds.ascent;
  341.     myfontinfo->max_descent = fontinfo->max_bounds.descent;
  342.     myfontinfo->dlist_base = 0;
  343.  
  344.     width = fontinfo->max_bounds.rbearing - fontinfo->min_bounds.lbearing;
  345.     height = fontinfo->max_bounds.ascent + fontinfo->max_bounds.descent;
  346.  
  347.     maxSpanLength = (width + 7) / 8;
  348.     /*
  349.      * Be careful determining the width of the pixmap; the X protocol allows
  350.      * pixmaps of width 2^16-1 (unsigned short size) but drawing coordinates
  351.      * max out at 2^15-1 (signed short size).  If the width is too large,
  352.      * we need to limit the glyphs per grab.
  353.      */
  354.     if ((glyphsPerGrab * 8 * maxSpanLength) >= (1 << 15)) {
  355.     glyphsPerGrab = (1 << 15) / (8 * maxSpanLength);
  356.     }
  357.     pixwidth = glyphsPerGrab * 8 * maxSpanLength;
  358.     offscreen = XCreatePixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)),
  359.                   pixwidth, height, 1);
  360.  
  361.     values.font = font;
  362.     values.background = 0;
  363.     values.foreground = 0;
  364.     xgc = XCreateGC(dpy, offscreen, GCFont | GCBackground | GCForeground, &values);
  365.  
  366.     XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
  367.     XSetForeground(dpy, xgc, 1);
  368.  
  369.     numToGrab = 0;
  370.     if (fontinfo->per_char == NULL) {
  371.     charinfo = &(fontinfo->min_bounds);
  372.     charWidth = charinfo->rbearing - charinfo->lbearing;
  373.     charHeight = charinfo->ascent + charinfo->descent;
  374.     spanLength = (charWidth + 7) / 8;
  375.     }
  376.     for (i = 0; i < numchars; i++) {
  377.     if (fontinfo->per_char != NULL) {
  378.         charinfo = &(fontinfo->per_char[i]);
  379.         charWidth = charinfo->rbearing - charinfo->lbearing;
  380.         charHeight = charinfo->ascent + charinfo->descent;
  381.         if (charWidth == 0 || charHeight == 0) {
  382.         /* Still must move raster pos even if empty character */
  383.         myfontinfo->glyph[i].width = 0;
  384.         myfontinfo->glyph[i].height = 0;
  385.         myfontinfo->glyph[i].xoffset = 0;
  386.         myfontinfo->glyph[i].yoffset = 0;
  387.         myfontinfo->glyph[i].advance = charinfo->width;
  388.         myfontinfo->glyph[i].bitmap = NULL;
  389.         goto PossiblyDoGrab;
  390.         }
  391.     }
  392.     grabList[numToGrab] = i;
  393.  
  394.     /* XXX is this right for large fonts? */
  395.     character.byte2 = (i + fontinfo->min_char_or_byte2) & 255;
  396.     character.byte1 = (i + fontinfo->min_char_or_byte2) >> 8;
  397.  
  398.     /*
  399.      * XXX we could use XDrawImageString16 which would also paint the
  400.      * backing rectangle but X server bugs in some scalable font
  401.      * rasterizers makes it more effective to do XFillRectangles to clear
  402.      * the pixmap and XDrawImage16 for the text.
  403.      */
  404.     XDrawString16(dpy, offscreen, xgc,
  405.               -charinfo->lbearing + 8 * maxSpanLength * numToGrab,
  406.               charinfo->ascent, &character, 1);
  407.  
  408.     numToGrab++;
  409.  
  410.       PossiblyDoGrab:
  411.  
  412.     if (numToGrab >= glyphsPerGrab || i == numchars - 1) {
  413.         image = XGetImage(dpy, offscreen,
  414.           0, 0, pixwidth, height, 1, XYPixmap);
  415.         for (j = 0; j < numToGrab; j++) {
  416.         thisglyph = grabList[j];
  417.         if (fontinfo->per_char != NULL) {
  418.             charinfo = &(fontinfo->per_char[thisglyph]);
  419.             charWidth = charinfo->rbearing - charinfo->lbearing;
  420.             charHeight = charinfo->ascent + charinfo->descent;
  421.             spanLength = (charWidth + 7) / 8;
  422.         }
  423.         bitmapData = calloc(height * spanLength, sizeof(char));
  424.         if (!bitmapData)
  425.             goto FreeFontAndReturn;
  426.                 DEBUG_GLYPH4("index %d, glyph %d (%d by %d)\n",
  427.             j, thisglyph + fontinfo->min_char_or_byte2, charWidth, charHeight);
  428.         for (y = 0; y < charHeight; y++) {
  429.             for (x = 0; x < charWidth; x++) {
  430.             /*
  431.              * XXX The algorithm used to suck across the font ensures
  432.              * that each glyph begins on a byte boundary.  In theory
  433.              * this would make it convienent to copy the glyph into
  434.              * a byte oriented bitmap.  We actually use the XGetPixel
  435.              * function to extract each pixel from the image which is
  436.              * not that efficient.  We could either do tighter packing
  437.              * in the pixmap or more efficient extraction from the
  438.              * image.  Oh well.
  439.              */
  440.             if (XGetPixel(image, j * maxSpanLength * 8 + x, charHeight - 1 - y)) {
  441.                 DEBUG_GLYPH("x");
  442.                 bitmapData[y * spanLength + x / 8] |= (1 << (x & 7));
  443.             } else {
  444.                 DEBUG_GLYPH(" ");
  445.             }
  446.             }
  447.             DEBUG_GLYPH("\n");
  448.         }
  449.         myfontinfo->glyph[thisglyph].width = charWidth;
  450.         myfontinfo->glyph[thisglyph].height = charHeight;
  451.         myfontinfo->glyph[thisglyph].xoffset = -charinfo->lbearing;
  452.         myfontinfo->glyph[thisglyph].yoffset = charinfo->descent;
  453.         myfontinfo->glyph[thisglyph].advance = charinfo->width;
  454.         myfontinfo->glyph[thisglyph].bitmap = bitmapData;
  455.         }
  456.         XDestroyImage(image);
  457.         numToGrab = 0;
  458.         /* do we need to clear the offscreen pixmap to get more? */
  459.         if (i < numchars - 1) {
  460.         XSetForeground(dpy, xgc, 0);
  461.         XFillRectangle(dpy, offscreen, xgc, 0, 0, 8 * maxSpanLength * glyphsPerGrab, height);
  462.         XSetForeground(dpy, xgc, 1);
  463.         }
  464.     }
  465.     }
  466.     XFreeGC(dpy, xgc);
  467.     XFreePixmap(dpy, offscreen);
  468.     return myfontinfo;
  469.  
  470.   FreeFontAndReturn:
  471.     XDestroyImage(image);
  472.     XFreeGC(dpy, xgc);
  473.     XFreePixmap(dpy, offscreen);
  474.     for (j = i - 1; j >= 0; j--) {
  475.     if (myfontinfo->glyph[j].bitmap)
  476.         free(myfontinfo->glyph[j].bitmap);
  477.     }
  478.     free(myfontinfo);
  479.     return NULL;
  480. }
  481.  
  482. void
  483. MakeCube(void)
  484. {
  485.     /*
  486.      * No back side to the cube is drawn since the animation makes sure the
  487.      * back side can never be visible.  The "wobble" function is constrained
  488.      * so not to rotate far enough around to reveal the back side.
  489.      */
  490.     glNewList(1, GL_COMPILE);
  491.     glBegin(GL_QUAD_STRIP);
  492.     /* back left post */
  493.     glColor3f(6.0, 0.5, 0.5);
  494.     glVertex3f(0, 0, 0);
  495.     glVertex3f(0, 1, 0);
  496.     /* front left post */
  497.     glVertex3f(0, 0, 1);
  498.     glVertex3f(0, 1, 1);
  499.     glColor3f(1.0, 0.0, 0.0);
  500.     /* front right post */
  501.     glVertex3f(1, 0, 1);
  502.     glVertex3f(1, 1, 1);
  503.     /* back right post */
  504.     glColor3f(6.0, 0.5, 0.5);
  505.     glVertex3f(1, 0, 0);
  506.     glVertex3f(1, 1, 0);
  507.     glEnd();
  508.     glBegin(GL_QUADS);
  509.     /* top face */
  510.     glVertex3f(1, 1, 0);
  511.     glVertex3f(1, 1, 1);
  512.     glVertex3f(0, 1, 1);
  513.     glVertex3f(0, 1, 0);
  514.     /* bottom face */
  515.     glVertex3f(1, 0, 0);
  516.     glVertex3f(1, 0, 1);
  517.     glVertex3f(0, 0, 1);
  518.     glVertex3f(0, 0, 0);
  519.     glEnd();
  520.     glEndList();
  521. }
  522.  
  523. void
  524. MakeGlyphDisplayList(FontInfoPtr font, int c)
  525. {
  526.     PerCharInfoPtr  glyph;
  527.     char           *bitmapData;
  528.     int             width, height, spanLength;
  529.     int             x, y;
  530.  
  531.     if (c < font->min_char || c > font->max_char)
  532.     return;
  533.     if (font->dlist_base == 0) {
  534.     font->dlist_base = glGenLists(font->max_char - font->min_char + 1);
  535.     if (font->dlist_base == 0)
  536.         XtAppError(app, "could not generate font display lists");
  537.     }
  538.     glyph = &font->glyph[c - font->min_char];
  539.     glNewList(c - font->min_char + font->dlist_base, GL_COMPILE);
  540.     bitmapData = glyph->bitmap;
  541.     if (bitmapData) {
  542.     int             oldx = 0, oldy = 0;
  543.  
  544.     glPushMatrix();
  545.     glTranslatef(-glyph->xoffset, -glyph->yoffset, 0);
  546.     width = glyph->width;
  547.     spanLength = (width + 7) / 8;
  548.     height = glyph->height;
  549.     for (x = 0; x < width; x++) {
  550.         for (y = 0; y < height; y++) {
  551.         if (bitmapData[y * spanLength + x / 8] & (1 << (x & 7))) {
  552.             int             y1, count;
  553.  
  554.                     /*
  555.              * Fonts tend to have good vertical repetion.  If we find that
  556.              * the vertically adjacent  pixels in the glyph bitmap are also enabled,
  557.              * we can scale a single cube instead of drawing a cube per pixel.
  558.              */
  559.             for (y1 = y + 1, count = 1; y < height; y1++, count++) {
  560.             if (!(bitmapData[y1 * spanLength + x / 8] & (1 << (x & 7))))
  561.                 break;
  562.             }
  563.             glTranslatef(x - oldx, y - oldy, 0);
  564.             oldx = x;
  565.             oldy = y;
  566.             if (count > 1) {
  567.             glPushMatrix();
  568.             glScalef(1, count, 1);
  569.             glCallList(1);
  570.             glPopMatrix();
  571.             y += count - 1;
  572.             } else {
  573.             glCallList(1);
  574.             }
  575.         }
  576.         }
  577.     }
  578.     glPopMatrix();
  579.     }
  580.     glTranslatef(glyph->advance, 0, 0);
  581.     glEndList();
  582. }
  583.  
  584. GLuint
  585. GetGlyphDisplayList(FontInfoPtr font, int c)
  586. {
  587.     PerCharInfoPtr  glyph;
  588.  
  589.     if (c < font->min_char || c > font->max_char)
  590.     return 0;
  591.     if (font->dlist_base == 0)
  592.     XtAppError(app, "font not display listed");
  593.     return c - font->min_char + font->dlist_base;
  594. }
  595.  
  596. MakeStringDisplayList(FontInfoPtr font, unsigned char *message, GLuint dlist)
  597. {
  598.     unsigned char  *c;
  599.  
  600.     for (c = message; *c != '\0'; c++) {
  601.     MakeGlyphDisplayList(font, *c);
  602.     }
  603.     glNewList(dlist, GL_COMPILE);
  604.     for (c = message; *c != '\0'; c++) {
  605.     glCallList(GetGlyphDisplayList(font, *c));
  606.     }
  607.     glEndList();
  608. }
  609.  
  610. int
  611. GetStringLength(FontInfoPtr font, unsigned char *message)
  612. {
  613.     unsigned char  *c;
  614.     int             ch;
  615.     int             width = 0;
  616.  
  617.     for (c = message; *c != '\0'; c++) {
  618.     ch = *c;
  619.     if (ch >= font->min_char && ch <= font->max_char) {
  620.         width += font->glyph[ch - font->min_char].advance;
  621.     }
  622.     }
  623.     return width;
  624. }
  625.  
  626. SetupMessageDisplayList(FontEntryPtr fontEntry, int num, char *message[])
  627. {
  628.     FontInfoPtr     fontinfo = fontEntry->fontinfo;
  629.     GLfloat         scaleFactor;
  630.     int             totalHeight, maxWidth, height, width;
  631.     int             i;
  632.  
  633.     if (!fontinfo) {
  634.     fontinfo = SuckGlyphsFromServer(dpy, fontEntry->xfont->fid);
  635.     fontEntry->fontinfo = fontinfo;
  636.     }
  637.     height = fontinfo->max_ascent + fontinfo->max_descent;
  638.     maxWidth = 0;
  639.     for (i = 0; i < num; i++) {
  640.     MakeStringDisplayList(fontinfo, message[i], base + i + 1);
  641.     width = GetStringLength(fontinfo, message[i]);
  642.     if (width > maxWidth)
  643.         maxWidth = width;
  644.     }
  645.  
  646. #define SHRINK_FACTOR 25.0    /* empirical */
  647.  
  648.     totalHeight = height * num - fontinfo->max_descent;
  649.     if (maxWidth > totalHeight) {
  650.     scaleFactor = SHRINK_FACTOR / maxWidth;
  651.     } else {
  652.     scaleFactor = SHRINK_FACTOR / totalHeight;
  653.     }
  654.  
  655.     glNewList(base, GL_COMPILE);
  656.     glScalef(scaleFactor, scaleFactor, 1); /* 1 in Z gives glyphs constant depth */
  657.     for (i = 0; i < num; i++) {
  658.     glPushMatrix();
  659.     width = GetStringLength(fontinfo, message[i]);
  660.     glTranslatef(-width / 2.0, height * (num - i - 1) - totalHeight / 2.0, 0);
  661.     glCallList(base + i + 1);
  662.     glPopMatrix();
  663.     }
  664.     glEndList();
  665. }
  666.  
  667. void
  668. fontSelect(Widget widget, XtPointer client_data, XmRowColumnCallbackStruct * cbs)
  669. {
  670.     XmToggleButtonCallbackStruct *state = (XmToggleButtonCallbackStruct *) cbs->callbackstruct;
  671.     FontEntryPtr    fontEntry = (FontEntryPtr) cbs->data;
  672.  
  673.     if (state->set) {
  674.     SetupMessageDisplayList(fontEntry, numMessages, messages);
  675.     if (!motion)
  676.         draw(glxarea);
  677.     }
  678. }
  679.  
  680. void
  681. neverCalled(void)
  682. {
  683. }
  684.  
  685. void
  686. main(int argc, char *argv[])
  687. {
  688.     int             i;
  689. #ifdef IRIX_5_1_MOTIF_BUG_WORKAROUND
  690.     /*
  691.      * XXX Unfortunately a bug in the IRIX 5.1 Motif shared library
  692.      * causes a BadMatch X protocol error if the SGI look&feel
  693.      * is enabled for this program.  If we detect we are on an
  694.      * IRIX 5.1 system, skip the first two fallback resources which
  695.      * specify using the SGI look&feel.
  696.      */
  697.     struct utsname versionInfo;
  698.  
  699.     if(uname(&versionInfo) >= 0) {
  700.         if(!strcmp(versionInfo.sysname, "IRIX") &&
  701.        !strncmp(versionInfo.release, "5.1", 3)) {
  702.             toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
  703.                        &fallbackResources[2], NULL, 0);
  704.         }
  705.     }
  706.     if(toplevel == NULL) {
  707.         toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
  708.                        fallbackResources, NULL, 0);
  709.     }
  710. #else
  711.     toplevel = XtAppInitialize(&app, "Textfun", NULL, 0, &argc, argv,
  712.                    fallbackResources, NULL, 0);
  713. #endif
  714.     dpy = XtDisplay(toplevel);
  715.     /* find an OpenGL-capable RGB visual with depth buffer */
  716.     vi = glXChooseVisual(dpy, DefaultScreen(dpy), dblBuf);
  717.     if (vi == NULL) {
  718.     vi = glXChooseVisual(dpy, DefaultScreen(dpy), snglBuf);
  719.     if (vi == NULL)
  720.         XtAppError(app, "no RGB visual with depth buffer");
  721.     doubleBuffer = GL_FALSE;
  722.     }
  723.     for (i = 0; i < NUM_FONT_ENTRIES; i++) {
  724.     fontEntry[i].xfont = XLoadQueryFont(dpy, fontEntry[i].xlfd);
  725.     if (i == 0 && !fontEntry[i].xfont)
  726.         XtAppError(app, "could not get basic font");
  727.     }
  728.  
  729.     fontEntry[0].fontinfo = SuckGlyphsFromServer(dpy, fontEntry[0].xfont->fid);
  730.     if (!fontEntry[0].fontinfo)
  731.     XtAppError(app, "could not get font glyphs");
  732.  
  733.     /* create an OpenGL rendering context */
  734.     cx = glXCreateContext(dpy, vi, /* no display list sharing */ None,
  735.                /* favor direct */ GL_TRUE);
  736.     if (cx == NULL)
  737.     XtAppError(app, "could not create rendering context");
  738.  
  739.     /* create an X colormap since probably not using default visual */
  740.     cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen),
  741.                vi->visual, AllocNone);
  742.     XtVaSetValues(toplevel, XtNvisual, vi->visual, XtNdepth, vi->depth,
  743.           XtNcolormap, cmap, NULL);
  744.     XtAddEventHandler(toplevel, StructureNotifyMask, False,
  745.               map_state_changed, NULL);
  746.     mainw = XmCreateMainWindow(toplevel, "mainw", NULL, 0);
  747.     XtManageChild(mainw);
  748.  
  749.     /* create menu bar */
  750.     menubar = XmCreateMenuBar(mainw, "menubar", NULL, 0);
  751.     XtManageChild(menubar);
  752.     /* hack around Xt's ignorance of visuals */
  753.     XtSetArg(menuPaneArgs[0], XmNdepth, DefaultDepthOfScreen(XtScreen(mainw)));
  754.     XtSetArg(menuPaneArgs[1],
  755.          XmNcolormap, DefaultColormapOfScreen(XtScreen(mainw)));
  756.  
  757.     /* create File pulldown menu: Quit */
  758.     menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 2);
  759.     btn = XmCreatePushButton(menupane, "Quit", NULL, 0);
  760.     XtAddCallback(btn, XmNactivateCallback, quit, NULL);
  761.     XtManageChild(btn);
  762.     XtSetArg(args[0], XmNsubMenuId, menupane);
  763.     cascade = XmCreateCascadeButton(menubar, "File", args, 1);
  764.     XtManageChild(cascade);
  765.  
  766.     /* create Options pulldown menu: Motion, Dolly, Rotate, Wobble */
  767.     menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 2);
  768.     btn = XmCreateToggleButton(menupane, "Motion", NULL, 0);
  769.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) toggle, NULL);
  770.     XtManageChild(btn);
  771.     btn = XmCreateToggleButton(menupane, "Dolly", NULL, 0);
  772.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) dolly, NULL);
  773.     XtVaSetValues(btn, XmNset, True, NULL);
  774.     XtManageChild(btn);
  775.     btn = XmCreateToggleButton(menupane, "Rotate", NULL, 0);
  776.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) rotate, NULL);
  777.     XtManageChild(btn);
  778.     btn = XmCreateToggleButton(menupane, "Wobble", NULL, 0);
  779.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) wobble, NULL);
  780.     XtManageChild(btn);
  781.     XtSetArg(args[0], XmNsubMenuId, menupane);
  782.     cascade = XmCreateCascadeButton(menubar, "Options", args, 1);
  783.     XtManageChild(cascade);
  784.  
  785.     XtSetArg(menuPaneArgs[2], XmNradioBehavior, True);
  786.     XtSetArg(menuPaneArgs[3], XmNradioAlwaysOne, True);
  787.     menupane = XmCreatePulldownMenu(menubar, "menupane", menuPaneArgs, 4);
  788.     XtAddCallback(menupane, XmNentryCallback, (XtCallbackProc) fontSelect, NULL);
  789.     for (i = 0; i < NUM_FONT_ENTRIES; i++) {
  790.     btn = XmCreateToggleButton(menupane, fontEntry[i].name, NULL, 0);
  791.     XtAddCallback(btn, XmNvalueChangedCallback, (XtCallbackProc) neverCalled, &fontEntry[i]);
  792.     if (i == 0)
  793.         XtVaSetValues(btn, XmNset, True, NULL);
  794.         if (!fontEntry[i].xfont)
  795.         XtSetSensitive(btn, False);
  796.     XtManageChild(btn);
  797.     }
  798.     XtSetArg(args[0], XmNsubMenuId, menupane);
  799.     cascade = XmCreateCascadeButton(menubar, "Font", args, 1);
  800.     XtManageChild(cascade);
  801.  
  802.     /* create framed drawing area for OpenGL rendering */
  803.     frame = XmCreateFrame(mainw, "frame", NULL, 0);
  804.     XtManageChild(frame);
  805.     glxarea = XtCreateManagedWidget("glxarea", xmDrawingAreaWidgetClass,
  806.                     frame, NULL, 0);
  807.     XtAddCallback(glxarea, XmNexposeCallback, (XtCallbackProc) draw, NULL);
  808.     XtAddCallback(glxarea, XmNresizeCallback, resize, NULL);
  809.     XtAddCallback(glxarea, XmNinputCallback, input, NULL);
  810.     /* set up application's window layout */
  811.     XmMainWindowSetAreas(mainw, menubar, NULL, NULL, NULL, frame);
  812.     XtRealizeWidget(toplevel);
  813.  
  814.     /*
  815.      * Once widget is realized (ie, associated with a created X window), we
  816.      * can bind the OpenGL rendering context to the window.
  817.      */
  818.     glXMakeCurrent(dpy, XtWindow(glxarea), cx);
  819.     made_current = GL_TRUE;
  820.     /* setup OpenGL state */
  821.     glEnable(GL_DEPTH_TEST);
  822.     glDepthFunc(GL_LEQUAL);
  823.     glClearDepth(1.0);
  824.     glMatrixMode(GL_PROJECTION);
  825.     glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 80);
  826.     glMatrixMode(GL_MODELVIEW);
  827.  
  828.     MakeCube();
  829.  
  830.     if (argv[1] != NULL) {
  831.     numMessages = argc - 1;
  832.     messages = &argv[1];
  833.     } else {
  834.     numMessages = NUM_DEFAULT_MESSAGES;
  835.     messages = defaultMessage;
  836.     }
  837.  
  838.     base = glGenLists(numMessages + 1);
  839.     SetupMessageDisplayList(&fontEntry[0], numMessages, messages);
  840.  
  841.     tick();
  842.  
  843.     /* start event processing */
  844.     XtAppMainLoop(app);
  845. }
  846.